/* Star IconBar menu
   Fri,21 Sep 1990
   Copyright C.T.Stretch 1990
*/

#include "wimp.h"
#include "wimpt.h"
#include "win.h"
#include "event.h"
#include "res.h"
#include "menu.h"
#include "template.h"
#include "dbox.h"
#include "dboxquery.h"
#include "werr.h"
#include "xferrecv.h"
#include "flex.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

#define MAXM 16
#define FIRSTITEM 2
#define MAXF 16
#define CMAX 255
#define EDNLEN 64
#define DEFTYPE 0x123456
#define TRANS 0
#define NOBAR (1<<30)
#define NOTRANS -1
#define SKIP for(;*s&&s<e;s++)

typedef struct comstr
{ char *fp;
  int co,mo,len,mn;
} comstr;

typedef struct fstr
{ char fn[CMAX+1];
  int type;
} fstr;

static comstr cd[MAXM];
static fstr files[MAXF];
static int fno=0;
static menu barmenu,starmenu,unmenu;
static int nextitem,nextmn;
static int ino;
static char icbuf[12]="0";
static char cbuf[CMAX+1],kbuf[CMAX+1];
static char *bufp;
static char *ce=cbuf+CMAX,*ke=kbuf+CMAX;
static int frep=0;
static char *openbr=0;
static int edhand=0;
static char edname[EDNLEN];
static BOOL ok=TRUE;
static int barend=-1;
static int trans;

static void barmenuproc(void *,char *);

static void err(char *m)
{ ok=FALSE;
  os_swi6(0x24,(int)"Star$Error",(int)m,strlen(m),0,0,0);
}

static int flen(char *fn)
{ os_filestr fstr;
  fstr.action=17;
  fstr.name=fn;
  if(os_file(&fstr)) return -1;
  return fstr.start;
}

static void setmf(menu m,int n,int f)
{ int p=(int)menu_syshandle(m)+sizeof(wimp_menuhdr);
  wimp_menuitem *mi=(wimp_menuitem*)p;
  mi[n-1].flags=f;
}

static BOOL newmenus(void)
{ barmenu=menu_new("Star","Star");
  starmenu=menu_new("Star",">Info,Load,Unload,>Error,Quit");
  unmenu=menu_new("UnloadMenu","All,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16");
  menu_submenu(starmenu,3,unmenu);
  menu_submenu(barmenu,1,starmenu);
  nextitem=FIRSTITEM;nextmn=FIRSTITEM;
  setmf(unmenu,1,wimp_MLAST);
  if(!event_attachmenu(win_ICONBAR,barmenu,barmenuproc,0)) return FALSE;
  return TRUE;
}

static char *additems(char *s,char *e,menu m,int *off)
{ int n;
  menu sub=0;
  for(;s<e;s++)
  { switch(*s)
    { case ':':menu_extend(m,++s);(*off)++;
               SKIP;
               break;
      case '{':menu_extend(m,++s);
               sub=menu_new(s,"");
               SKIP;n=1;
               s=additems(++s,e,sub,&n);
               menu_submenu(m,(*off)++,sub);
               break;
      case '}':SKIP;return s;
      case ';':case  0 :case ' ':case '*':case '>':case '+':
      case '?':case '|':case '(':case ')':case '[':
      case ']':case '<':case '#':case '!':
               SKIP;break;
       default:werr(0,"odd line |%s| %d",s,*s);SKIP;
    }
  }
  return e;
}

static void nullify(char *s,char *e)
{ for(;s<e;s++) if(*s==10||*s==13) *s=0;
}

static void setmenu(comstr *cs)
{ char *c,*s;
  int bar=nextitem;
  cs->mn=nextmn++;
  cs->mo=nextitem;
  additems(cs->fp+cs->co,cs->fp+cs->len,barmenu,&nextitem);
  c=(char*)&(((wimp_menuitem*)
  ((int)(menu_syshandle(unmenu))+sizeof(wimp_menuhdr)))[cs->mn-1].data);
  for(s=cs->fp+strlen(cs->fp);*s!='.';s--);strncpy(c,s+1,12);
  setmf(unmenu,cs->mn-1,0);setmf(unmenu,cs->mn,wimp_MLAST);
  if(nextitem>bar) setmf(barmenu,bar-1,wimp_MSEPARATE);
}

static void addmenu(char *fn)
{ int n,len;
  FILE *file;
  comstr *cs;
  for(n=0;n<MAXM;n++) if(!cd[n].mo)break;
  if(n>=MAXM) return;
  cs=cd+n;
  cs->co=strlen(fn)+1;
  len=flen(fn);
  if(len<=0) return;
  if(!flex_alloc((flex_ptr)&(cs->fp),cs->co+len+1)) return;
  strcpy(cs->fp,fn);
  file=fopen(fn,"rb");
  fread(cs->fp+cs->co,1,len,file);
  fclose(file);
  cs->len=cs->co+len;
  if(cs->fp[cs->len-1]!=10) cs->fp[cs->len++]=10;
  nullify(cs->fp+cs->co,cs->fp+cs->len);
  setmenu(cs);
}

static char *skipsub(char *s,char *e)
{ for(;s<e;s++)
  { switch(*s)
    { case '{':SKIP;
               s=skipsub(++s,e);
               break;
      case '}':SKIP;return s;
       default:SKIP;
    }
  }
  return e;
}

static char *findentry(char *h,char *s,char *e)
{ int n=0;
  for(;s<e&&ok;s++)
  { switch(*s)
    { case ':':n+=1;
               SKIP;
               if(n>=h[0]) return ++s;
               break;
      case '{':n+=1;
               SKIP;
               if(n>=h[0]) return findentry((*h)?h+1:h,++s,e);
               s=skipsub(++s,e);
               break;
      case '}':return 0;
       default:SKIP;
    }
  }
  return 0;
}


static void add(char c)
{ if(bufp<ke) *bufp++=c;
  else err("Line too long");
}

static void adds(char *s)
{ if(bufp+strlen(s)<=ke)
  { strcpy(bufp,s);
    bufp+=strlen(s);
  }
  else err("Line too long");
}

static void addf(int n,char *p)
{ char *f=files[n].fn;
  char *e=f+strlen(f);
  char *q,c;
  for(;*p=='<'||*p=='>';p++)
  { for(q=e-1;q>f;q--) if(*q=='.')break;
    if(*p=='<') e=q; else f=q+1;
  }
  if(f>=e) { err("Bad file part");return;}
  c=*e;*e=0;adds(f);*e=c;
}

static void subst(char *s,char *t)
{ int n;
  char *c,*p,tbuf[CMAX];
  dbox d;
  bufp=kbuf;
  for(;*s&&ok;s++)
  { if(*s=='%')
    { for(p=++s;*s=='<'||*s=='>';s++);
      switch(*s)
      { case '!':for(n=0;n<fno;n++){ addf(n,p);add(' ');}
                 break;
        case '?':if(!openbr) err("? not in brackets");
                 if(ok) addf(frep,p);
                 break;
        case '"':for(c=++s;*s;s++) if(*s=='"') break;
                 if(*s!='"'){err("missing \"");break;}
                 d=dbox_new("prompt");
                 if(!d) {err("Cant open prompt box");break;};
                 *s=0;dbox_setfield(d,2,c);*s='"';
                 dbox_show(d);
                 if(dbox_fillin(d)) { dbox_dispose(&d);err("Canceled");break;}
                 dbox_getfield(d,1,tbuf,CMAX);adds(tbuf);
                 dbox_dispose(&d);
                 break;
        case '%':add('%');break;
        default:n=*s-'0';
                if(n>=0&&n<=9)
                { if(files[n].type>=0) addf(n,p);
                  else err("Too few files");
                }
                else err("Odd % substitution");
      }
    }
    else add(*s);
  }
  add(0);
  if(ok)
  { if(trans!=NOTRANS)
    { os_swi3r(0x27,(int)kbuf,(int)t,(ce-t)|trans,0,0,&n);
      if(n>ce-t) err("Line too long");
    }
    else
    { if(strlen(kbuf)<ce-t) strcpy(t,kbuf);else err("Line too long");
    }
  }
}

static char *nextopen(char *s,char *e)
{ for(;s<e&&*s!=')';s++) SKIP;
  if(*s!=')') err("Missing close bracket");
  return s;
}

static char *dofile(char *s,char *e)
{ BOOL manyfile=(openbr!=0);
  FILE *file=fopen("<Star$Scrap>.Temp","wb");
  if(!file) {err("Cant open scrap file");return 0;}
  for(;s<e&&ok;s++)
  { trans=NOTRANS;
    if(*s=='|'){trans=TRANS;s++;}
    if(*s=='!'){trans=NOBAR;s++;}
    switch(*s)
    { case '*':subst(++s,cbuf);
               if(ok) { fputs(cbuf,file);fputc(10,file);}
               SKIP;break;
      case '(':if(manyfile||openbr) err("Odd open bracket");
               if(isdigit(s[1])) frep=*(++s)-'0';
               else frep=0;
               if(frep>=fno) s=nextopen(s,e);
               openbr=++s;SKIP;
               break;
      case ')':if(manyfile||!openbr) err("Odd close bracket");
               if(++frep<fno) s=openbr;else openbr=0;
               SKIP;break;
      case ']':fclose(file);
               system("settype <Star$Scrap>.Temp FEB");
               strcpy(cbuf,"Obey <Star$Scrap>.Temp ");
               subst(++s,cbuf+strlen(cbuf));
               if(ok) wimp_starttask(cbuf);
               SKIP;return s;
      case ':':case '{':case '}':s=e;break;;
      case ';':case ' ':case 0:SKIP;break;
      default:err("Strange line");
    }
  }
  fclose(file);
  return 0;
}

static void edfile(char *s)
{ wimp_msgstr m;
  subst(s,cbuf);
  if(ok)
  { if(edhand)
    { m.hdr.size=256;
      m.hdr.your_ref=0;
      m.hdr.action=wimp_MDATALOAD;
      m.data.dataload.w=(wimp_w)-1;
      m.data.dataload.i=0;
      m.data.dataload.x=0;
      m.data.dataload.y=0;
      m.data.dataload.size=0;
      m.data.dataload.type=0xFFF;
      strcpy(m.data.dataload.name,cbuf);
      wimpt_noerr(wimp_sendmessage(wimp_ESENDWANTACK,&m,edhand));
    }
    else
    { if(strlen(cbuf)<252)
      { sprintf(kbuf,"run %s",cbuf);
        wimp_starttask(kbuf);
      }
      else err("Line too long");
    }
  }
}

static void key(char *s)
{ char *c;
  int b,n=0;
  subst(s,cbuf);
  if(ok) for(c=cbuf;*c;c++){ b=*c; os_byte(138,&n,&b);}
}


static void typecheck(char *s,int n)
{ int t=files[n].type;
  for(;*s;s++) if(strtol(s,&s,16)==t) return;
  err("Bad file type");
}

static void checktype(char *s)
{ int n;
  switch(*s)
  { case '?':typecheck(++s,frep);break;
    case '!':for(n=0;n<fno;n++) typecheck(++s,n);break;
    default:if(isdigit(*s)) typecheck(++s,*s-'0');else err("Bad # suffix");
  }
}

static void doit(char *s,char *e)
{ int m,n;
  openbr=0;
  for(;s<e&&ok;s++)
  { trans=NOTRANS;
    if(*s=='|'){trans=TRANS;s++;}
    if(*s=='!'){trans=NOBAR;s++;}
    switch(*s)
    { case '*':subst(++s,cbuf);
               if(ok) wimp_starttask(cbuf);
               break;
      case '>':edfile(++s);break;
      case '<':n=s[1];m=n-'0';
               if(!isdigit(n)) err("Bad default");
               else if(files[m].type<0)
               { subst(s+2,cbuf);
                 if(ok)
                 { strcpy(files[m].fn,cbuf);
                   files[m].type=DEFTYPE;
                 }
               }
               break;
      case '+':key(++s);break;
      case '#':checktype(++s);break;
      case '?':subst(++s,cbuf);
               if(ok&&dboxquery(cbuf)!=dboxquery_YES) err("Canceled");
               break;
      case '(':if(openbr) err("Odd open bracket\n");
               if(isdigit(s[1])) frep=*(++s)-'0';
               else frep=0;
               if(frep>=fno) s=nextopen(s,e);
               openbr=++s;break;
      case ')':if(!openbr) err("Odd close bracket\n");
               if(++frep<fno)s=openbr;else openbr=0;
               break;
      case '[':SKIP;s=dofile(s,e);
               break;
      case ':':case '{':case '}':s=e;break;;
      case ';':case ' ':case 0:break;
      default:err("Strange line");
    }
    SKIP;
  }
}

static void findcom(char *h)
{ int n,k=0;
  char *s;
  comstr *cs=0;
  for(n=0;n<MAXM;n++) if(cd[n].mo&&cd[n].mo<=h[0])
  if(cd[n].mo>k) { cs=cd+n;k=cs->mo;}
  if(!k) { werr(0,"Can't find comstr %d",h[0]);return;}
  h[0]-=k-1;
  s=findentry(h,cs->fp+cs->co,cs->fp+cs->len);
  if(!s){ werr(0,"not found");return;}
  doit(s,cs->fp+cs->len);
}

static BOOL barhand(wimp_eventstr *e,void *v)
{ char *fn;
  int ftype;
  v=v;
  switch(e->e)
  { case wimp_ESEND:case wimp_ESENDWANTACK:
    switch(e->data.msg.hdr.action)
    { case wimp_MDATALOAD:ftype=xferrecv_checkinsert(&fn);
                          if(strlen(fn)>CMAX||fno>=MAXF) break;
                          strcpy(files[fno].fn,fn);files[fno].type=ftype;
                          sprintf(icbuf,"%d",++fno);
                        wimpt_noerr(wimp_set_icon_state((wimp_w)-1,ino,0,0));
                          break;
      case wimp_MINITTASK:if(!strcmp(edname,e->data.msg.data.chars+8))
                            edhand=e->data.msg.hdr.task;
                          return TRUE;
      case wimp_MCLOSETASK:if(e->data.msg.hdr.task==edhand) edhand=0;
    }
    break;
  }
  return FALSE;
}

static void barclick(wimp_eventstr *e,void *h) { e=e;h=h;}

static void unload(int n)
{ int m;
  event_attachmenu(win_ICONBAR,0,barmenuproc,0);
  if(n==1)
  { for(m=0;m<MAXM;m++) if(cd[m].mo)
    { flex_free((flex_ptr)&(cd[m].fp));
      cd[m].mo=0;
    }
  }
  else
  { for(m=0;m<MAXM;m++)if(cd[m].mo&&cd[m].mn==n)
    { flex_free((flex_ptr)&(cd[m].fp));
      cd[m].mo=0;
    }
  }
  menu_dispose(&barmenu,TRUE);
  newmenus();
  for(m=0;m<MAXM;m++) if(cd[m].mo) setmenu(cd+m);
}

static void starmenuproc(char *h)
{ dbox db;
  int n;
  switch (h[1])
  { case 1:if(!h[2])break;
           db=dbox_new("Info");
           if(db)
           { dbox_setfield(db,4,"0.0 "__DATE__);
             dbox_show(db);dbox_fillin(db);
             dbox_dispose(&db);
           }
           break;
    case 2:for(n=0;n<fno;n++) if(files[n].type==0xFFF) addmenu(files[n].fn);
           break;
    case 3:unload(h[2]);break;
    case 4:if(!h[2]) break;
           db=dbox_new("error");
           if(db)
           { os_read_var_val("Star$Error",cbuf,CMAX);
             dbox_setfield(db,0,cbuf);
             dbox_show(db);dbox_fillin(db);
             dbox_dispose(&db);
           }
           break;
    case 5:exit(0);
  }
}

static void barmenuproc(void *wh,char *h)
{ int n;
  wh=wh;
  if(h[0]<FIRSTITEM) starmenuproc(h);
  else findcom(h);
  fno=0;strcpy(icbuf,ok?"0":"!?");
  for(n=0;n<MAXF;n++) files[n].type=-1;
  wimpt_noerr(wimp_set_icon_state((wimp_w)-1,ino,0,0));
  ok=TRUE;
}

static void mybaricon(void)
{ wimp_icreate cr[1];
  cr->w=(wimp_w)barend;
  cr->i.box.x0=0;cr->i.box.x1=68;
  cr->i.box.y0=-16;cr->i.box.y1=84;
  cr->i.flags=wimp_ISPRITE|wimp_INDIRECT|(wimp_IBTYPE*wimp_BCLICKDEBOUNCE);
  cr->i.flags|=wimp_ITEXT|(wimp_IFORECOL*7)|wimp_IFILLED|wimp_IHCENTRE;
  cr->i.flags|=wimp_IBACKCOL;
  cr->i.data.indirecttext.buffer=icbuf;
  cr->i.data.indirecttext.validstring="S!star";
  cr->i.data.indirecttext.bufflen=12;
  wimpt_noerr(wimp_create_icon(cr,&ino));
  win_register_event_handler(win_ICONBAR,barclick,0);
  win_activeinc();
}

static BOOL init(void)
{ wimpt_init("Star");
  res_init("Star");
  template_init();
  dbox_init();
  flex_init();
  mybaricon();
  if(!newmenus())werr(0,"Failed");
  win_register_event_handler(win_ICONBARLOAD,(win_event_handler)barhand,0);
  win_add_unknown_event_processor(barhand,0);
  os_read_var_val("Star$Ed",edname,EDNLEN);
  if(!edname[0]) strcpy(edname,"Edit");
  return TRUE;
}

int main(int n,char **c)
{ int i=1;
  if(n>1&&*(c[1])=='-') {i++;barend=-2;}
  if(!init()) return 0;
  for(;i<n;i++) addmenu(c[i]);
  for(;;) event_process();
  return 0;
}
